1 module hip.hiprenderer.backend.metal.mtlrenderer; 2 3 version(AppleOS): 4 5 import metal; 6 import hip.hiprenderer.renderer; 7 import hip.windowing.window; 8 import hip.hiprenderer.backend.metal.mtlshader; 9 import hip.hiprenderer.backend.metal.mtlvertex; 10 import hip.hiprenderer.backend.metal.mtltexture; 11 12 13 14 15 16 MTLCompareFunction fromHipDepthTestingFunction(HipDepthTestingFunction fn) 17 { 18 final switch(fn) with(HipDepthTestingFunction) 19 { 20 case Always: return MTLCompareFunction.Always; 21 case Never: return MTLCompareFunction.Never; 22 case Equal: return MTLCompareFunction.Equal; 23 case NotEqual: return MTLCompareFunction.NotEqual; 24 case Less: return MTLCompareFunction.Less; 25 case LessEqual: return MTLCompareFunction.LessEqual; 26 case Greater: return MTLCompareFunction.Greater; 27 case GreaterEqual: return MTLCompareFunction.GreaterEqual; 28 } 29 } 30 31 package MTLCommandBuffer defaultCommandBuffer(MTLCommandQueue cQueue) 32 { 33 static MTLCommandBufferDescriptor desc; 34 if(desc is null) 35 { 36 desc = MTLCommandBufferDescriptor.alloc.initialize; 37 desc.errorOptions = MTLCommandBufferErrorOption.EncoderExecutionStatus; 38 } 39 return cQueue.commandBuffer(desc); 40 } 41 42 private string getGPUFamily(MTLDevice device) 43 { 44 string ret = ""; 45 static foreach(mem; __traits(allMembers, MTLGPUFamily)) 46 { 47 if(device.supportsFamily(__traits(getMember, MTLGPUFamily, mem))) 48 { 49 if(ret != "") ret~= ", "; 50 ret~= mem; 51 } 52 } 53 if(ret == "") 54 return "unknown"; 55 return ret; 56 } 57 58 /** 59 * Used to be sent as a way to interface with ShaderVar uniforms. 60 * Whenever needing to bind more than one texture. 61 */ 62 package struct HipMTLShaderVarTexture 63 { 64 MTLTexture[16] textures; 65 MTLSamplerState[16] samplers; 66 } 67 68 class HipMTLRenderer : IHipRendererImpl 69 { 70 MTKView view; 71 MTLDevice device; 72 MTLCommandBuffer cmdBuffer; 73 MTLCommandQueue cmdQueue; 74 MTLRenderPassDescriptor renderPassDescriptor; 75 MTLRenderCommandEncoder cmdEncoder; 76 MTLPrimitiveType primitiveType; 77 78 package MTLArgumentBuffersTier argsTier; 79 80 //Descriptors 81 MTLDepthStencilDescriptor depthStencilDescriptor; 82 // 83 84 public bool init(HipWindow window) 85 { 86 view = cast(MTKView)window.MTKView; 87 device = view.device(); 88 cmdQueue = device.newCommandQueue(); 89 cmdQueue.label = "HipremeRendererQueue".ns; 90 // argsTier = device.argumentBuffersSupport; 91 ///Experimental Tier2 conditionals support, since I can't test them. 92 argsTier = MTLArgumentBuffersTier.Tier1; 93 94 import hip.console.log; 95 { 96 MTLCompileOptions _temp = MTLCompileOptions.alloc.ini; 97 98 loglnInfo( 99 "GPU Name: ", device.name, "\n", 100 "GPU Family: ", getGPUFamily(device), "\n", 101 "Metal Version: ", _temp.languageVersion, "\n", 102 "ArgumentBuffer: ", argsTier 103 ); 104 _temp.dealloc; 105 } 106 107 108 depthStencilDescriptor = MTLDepthStencilDescriptor.alloc.ini; 109 renderPassDescriptor = view.currentRenderPassDescriptor; 110 if(renderPassDescriptor is null) 111 throw new Error("Could not get a render pass descriptor."); 112 renderPassDescriptor.depthAttachment.clearDepth = 1.0; 113 114 hiplog(renderPassDescriptor.depthAttachment.level); 115 renderPassDescriptor.depthAttachment.loadAction = MTLLoadAction.Clear; 116 117 return cmdQueue !is null; 118 } 119 120 /** 121 * Util for creating MTLResourceOptions.StorageModePrivate buffer with initial data. 122 */ 123 package static MTLBuffer createPrivateBufferWithData(MTLCommandQueue cQueue, const(void)* data, size_t size) 124 { 125 MTLDevice d = cQueue.device; 126 MTLBuffer temp = d.newBuffer(data, size, MTLResourceOptions.StorageModeShared); 127 MTLBuffer ret = d.newBuffer(size, MTLResourceOptions.StorageModePrivate); 128 MTLCommandBuffer _cmdBuffer = cQueue.defaultCommandBuffer(); 129 MTLBlitCommandEncoder _cmdEncoder = _cmdBuffer.blitCommandEncoder; 130 131 _cmdEncoder.copyFromBuffer(temp, 0, ret, 0, size); 132 _cmdEncoder.endEncoding(); 133 _cmdBuffer.commit(); 134 _cmdBuffer.waitUntilCompleted(); 135 if(_cmdBuffer.error) 136 { 137 NSLog("Command Buffer Error %@".ns, _cmdBuffer.error); 138 } 139 return ret; 140 } 141 142 143 public bool initExternal() 144 { 145 return bool.init; // TODO: implement 146 } 147 148 public bool isRowMajor(){return true;} 149 void setErrorCheckingEnabled(bool enable = true){} 150 public Shader createShader() 151 { 152 return new Shader(new HipMTLShader(device, this)); 153 } 154 155 ShaderVar* createShaderVar(ShaderTypes shaderType, UniformType uniformType, string varName, size_t length) 156 { 157 if(argsTier == MTLArgumentBuffersTier.Tier1) return null; 158 159 switch(uniformType) with(UniformType) 160 { 161 case texture_array: 162 { 163 size_t single = (MTLTexture.sizeof + MTLSamplerState.sizeof); 164 return ShaderVar.createBlackboxed(shaderType, varName, uniformType, length*single, single); 165 } 166 default: return null; 167 } 168 } 169 170 171 172 public IHipFrameBuffer createFrameBuffer(int width, int height) 173 { 174 return IHipFrameBuffer.init; // TODO: implement 175 } 176 177 public IHipVertexArrayImpl createVertexArray() 178 { 179 return new HipMTLVertexArray(device, this); 180 } 181 182 public IHipTexture createTexture() 183 { 184 return new HipMTLTexture(device,cmdQueue, this); 185 } 186 187 public IHipVertexBufferImpl createVertexBuffer(size_t size, HipBufferUsage usage) 188 { 189 return new HipMTLVertexBuffer(device, cmdQueue, size, usage); 190 } 191 192 package pragma(inline, true) MTLRenderCommandEncoder getEncoder() { return cmdEncoder; } 193 194 public IHipIndexBufferImpl createIndexBuffer(index_t count, HipBufferUsage usage) 195 { 196 return new HipMTLIndexBuffer(device, cmdQueue, count, usage); 197 } 198 199 public int queryMaxSupportedPixelShaderTextures() 200 { 201 return 8;///Max used in SpritebatchShader. 202 } 203 204 public void setColor(ubyte r = 255, ubyte g = 255, ubyte b = 255, ubyte a = 255) 205 { 206 renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor( 207 r/255.0f, 208 g/255.0f, 209 b/255.0f, 210 a/255.0f 211 ); 212 } 213 214 public void setViewport(Viewport v) 215 { 216 cmdEncoder.setViewport(MTLViewport(v.x, v.y, v.width, v.height, 0, 1)); 217 } 218 219 public bool setWindowMode(HipWindowMode mode) 220 { 221 return bool.init; // TODO: implement 222 } 223 224 public bool isBlendingEnabled() const 225 { 226 return bool.init; // TODO: implement 227 } 228 229 public void setDepthTestingEnabled(bool enabled) 230 { 231 depthStencilDescriptor.depthWriteEnabled = enabled; 232 } 233 public void setDepthTestingFunction(HipDepthTestingFunction d) 234 { 235 assert(HipRenderer.isDepthTestingEnabled, "DepthTesting must be enabled before setting the function."); 236 depthStencilDescriptor.depthCompareFunction = d.fromHipDepthTestingFunction; 237 cmdEncoder.setDepthStencilState( 238 device.newDepthStencilStateWithDescriptor(depthStencilDescriptor) 239 ); 240 } 241 public void setStencilTestingEnabled(bool bEnable) 242 { 243 } 244 245 public void setStencilTestingMask(uint mask) 246 { 247 } 248 public void setColorMask(ubyte r, ubyte g, ubyte b, ubyte a) 249 { 250 251 } 252 253 public void setStencilTestingFunction(HipStencilTestingFunction passFunc, uint reference, uint mask) 254 { 255 } 256 257 public void setStencilOperation(HipStencilOperation stencilFail, HipStencilOperation depthFail, HipStencilOperation stencilAndDephPass) 258 { 259 } 260 261 public bool hasErrorOccurred(out string err, string file = __FILE__, size_t line = __LINE__) 262 { 263 return bool.init; // TODO: implement 264 } 265 266 public void begin() 267 { 268 cmdBuffer = cmdQueue.defaultCommandBuffer(); 269 cmdBuffer.label = "HipremeRenderer".ns; 270 cmdEncoder = cmdBuffer.renderCommandEncoderWithDescriptor(view.currentRenderPassDescriptor); 271 renderPassDescriptor = view.currentRenderPassDescriptor; 272 } 273 274 public void setRendererMode(HipRendererMode mode) 275 { 276 final switch(mode) 277 { 278 case HipRendererMode.LINE: 279 primitiveType = MTLPrimitiveType.Line; break; 280 case HipRendererMode.LINE_STRIP: 281 primitiveType = MTLPrimitiveType.LineStrip; break; 282 case HipRendererMode.POINT: 283 primitiveType = MTLPrimitiveType.Point; break; 284 case HipRendererMode.TRIANGLES: 285 primitiveType = MTLPrimitiveType.Triangle; break; 286 case HipRendererMode.TRIANGLE_STRIP: 287 primitiveType = MTLPrimitiveType.TriangleStrip; break; 288 } 289 } 290 291 public void drawIndexed(index_t count, uint offset = 0) 292 { 293 enum IndexType = is(index_t == ushort) ? MTLIndexType.UInt16 : MTLIndexType.UInt32; 294 cmdEncoder.drawIndexedPrimitives(primitiveType, count, IndexType, boundIndexBuffer, offset*index_t.sizeof); 295 296 } 297 298 public void drawVertices(index_t count, uint offset = 0) 299 { 300 cmdEncoder.drawPrimitives(primitiveType, offset, count); 301 } 302 303 public void end() 304 { 305 cmdEncoder.endEncoding(); 306 cmdBuffer.presentDrawable(view.currentDrawable); 307 cmdBuffer.commit(); 308 cmdBuffer.waitUntilCompleted(); 309 } 310 311 public void clear() 312 { 313 renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadAction.Clear; 314 cmdEncoder.endEncoding(); 315 cmdBuffer.commit(); 316 begin(); 317 } 318 319 public void clear(ubyte r = 255, ubyte g = 255, ubyte b = 255, ubyte a = 255) 320 { 321 // setColor(r,g,b,a); 322 // renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadAction.Clear; 323 // cmdEncoder.endEncoding(); 324 // cmdBuffer.commit(); 325 // begin(); 326 } 327 328 public void dispose() 329 { 330 331 } 332 }